<template>
    <view class="uni-forms-item"
        :class="['is-direction-' + localLabelPos ,border?'uni-forms-item--border':'' ,border && isFirstBorder?'is-first-border':'']">
        <slot name="label">
            <view class="uni-forms-item__label" :class="{'no-label':!label && !required}"
                :style="{width:localLabelWidth,justifyContent: localLabelAlign}">
                <text v-if="required" class="is-required">*</text>
                <text>{{label}}</text>
            </view>
        </slot>
        <!-- #ifndef APP-NVUE -->
        <view class="uni-forms-item__content">
            <slot></slot>
            <view class="uni-forms-item__error" :class="{'msg--active':msg}">
                <text>{{msg}}</text>
            </view>
        </view>
        <!-- #endif -->
        <!-- #ifdef APP-NVUE -->
        <view class="uni-forms-item__nuve-content">
            <view class="uni-forms-item__content">
                <slot></slot>
            </view>
            <view class="uni-forms-item__error" :class="{'msg--active':msg}">
                <text class="error-text">{{msg}}</text>
            </view>
        </view>
        <!-- #endif -->
    </view>
</template>

<script>
    /**
     * uni-fomrs-item 表单子组件
     * @description uni-fomrs-item 表单子组件，提供了基础布局已经校验能力
     * @tutorial https://ext.dcloud.net.cn/plugin?id=2773
     * @property {Boolean} required 是否必填，左边显示红色"*"号
     * @property {String } 	label 				输入框左边的文字提示
     * @property {Number } 	labelWidth 			label的宽度，单位px（默认70）
     * @property {String } 	labelAlign = [left|center|right] label的文字对齐方式（默认left）
     * 	@value left		label 左侧显示
     * 	@value center	label 居中
     * 	@value right	label 右侧对齐
     * @property {String } 	errorMessage 		显示的错误提示内容，如果为空字符串或者false，则不显示错误信息
     * @property {String } 	name 				表单域的属性名，在使用校验规则时必填
     * @property {String } 	leftIcon 			【1.4.0废弃】label左边的图标，限 uni-ui 的图标名称
     * @property {String } 	iconColor 		【1.4.0废弃】左边通过icon配置的图标的颜色（默认#606266）
     * @property {String} validateTrigger = [bind|submit|blur]	【1.4.0废弃】校验触发器方式 默认 submit
     * 	@value bind 	发生变化时触发
     * 	@value submit 提交时触发
     * 	@value blur 	失去焦点触发
     * @property {String } 	labelPosition = [top|left] 【1.4.0废弃】label的文字的位置（默认left）
     * 	@value top	顶部显示 label
     * 	@value left	左侧显示 label
     */

    export default {
        name: 'uniFormsItem',
        options: {
            // #ifdef MP-TOUTIAO
            virtualHost: false,
            // #endif
            // #ifndef MP-TOUTIAO
            virtualHost: true
            // #endif
        },
        provide() {
            return {
                uniFormItem: this
            }
        },
        inject: {
            form: {
                from: 'uniForm',
                default: null
            },
        },
        props: {
            // 表单校验规则
            rules: {
                type: Array,
                default () {
                    return null;
                }
            },
            // 表单域的属性名，在使用校验规则时必填
            name: {
                type: [String, Array],
                default: ''
            },
            required: {
                type: Boolean,
                default: false
            },
            label: {
                type: String,
                default: ''
            },
            // label的宽度
            labelWidth: {
                type: [String, Number],
                default: ''
            },
            // label 居中方式，默认 left 取值 left/center/right
            labelAlign: {
                type: String,
                default: ''
            },
            // 强制显示错误信息
            errorMessage: {
                type: [String, Boolean],
                default: ''
            },
            // 1.4.0 弃用，统一使用 form 的校验时机
            // validateTrigger: {
            // 	type: String,
            // 	default: ''
            // },
            // 1.4.0 弃用，统一使用 form 的label 位置
            // labelPosition: {
            // 	type: String,
            // 	default: ''
            // },
            // 1.4.0 以下属性已经废弃，请使用  #label 插槽代替
            leftIcon: String,
            iconColor: {
                type: String,
                default: '#606266'
            },
        },
        data() {
            return {
                errMsg: '',
                userRules: null,
                localLabelAlign: 'left',
                localLabelWidth: '70px',
                localLabelPos: 'left',
                border: false,
                isFirstBorder: false,
            };
        },
        computed: {
            // 处理错误信息
            msg() {
                return this.errorMessage || this.errMsg;
            }
        },
        watch: {
            // 规则发生变化通知子组件更新
            'form.formRules'(val) {
                // TODO 处理头条vue3 watch不生效的问题
                // #ifndef MP-TOUTIAO
                this.init()
                // #endif
            },
            'form.labelWidth'(val) {
                // 宽度
                this.localLabelWidth = this._labelWidthUnit(val)

            },
            'form.labelPosition'(val) {
                // 标签位置
                this.localLabelPos = this._labelPosition()
            },
            'form.labelAlign'(val) {

            }
        },
        created() {
            this.init(true)
            if (this.name && this.form) {
                // TODO 处理头条vue3 watch不生效的问题
                // #ifdef MP-TOUTIAO
                this.$watch('form.formRules', () => {
                    this.init()
                })
                // #endif

                // 监听变化
                this.$watch(
                    () => {
                        const val = this.form._getDataValue(this.name, this.form.localData)
                        return val
                    },
                    (value, oldVal) => {
                        const isEqual = this.form._isEqual(value, oldVal)
                        // 简单判断前后值的变化，只有发生变化才会发生校验
                        // TODO  如果 oldVal = undefined ，那么大概率是源数据里没有值导致 ，这个情况不哦校验 ,可能不严谨 ，需要在做观察
                        // fix by mehaotian 暂时取消 && oldVal !== undefined ，如果formData 中不存在，可能会不校验
                        if (!isEqual) {
                            const val = this.itemSetValue(value)
                            this.onFieldChange(val, false)
                        }
                    }, {
                        immediate: false
                    }
                );
            }

        },
        // #ifndef VUE3
        destroyed() {
            if (this.__isUnmounted) return
            this.unInit()
        },
        // #endif
        // #ifdef VUE3
        unmounted() {
            this.__isUnmounted = true
            this.unInit()
        },
        // #endif
        methods: {
            /**
             * 外部调用方法
             * 设置规则 ，主要用于小程序自定义检验规则
             * @param {Array} rules 规则源数据
             */
            setRules(rules = null) {
                this.userRules = rules
                this.init(false)
            },
            // 兼容老版本表单组件
            setValue() {},
            /**
             * 外部调用方法
             * 校验数据
             * @param {any} value 需要校验的数据
             * @param {boolean} 是否立即校验
             * @return {Array|null} 校验内容
             */
            async onFieldChange(value, formtrigger = true) {
                const {
                    formData,
                    localData,
                    errShowType,
                    validateCheck,
                    validateTrigger,
                    _isRequiredField,
                    _realName
                } = this.form
                const name = _realName(this.name)
                if (!value) {
                    value = this.form.formData[name]
                }
                // fixd by mehaotian 不在校验前清空信息，解决闪屏的问题
                // this.errMsg = '';

                // fix by mehaotian 解决没有检验规则的情况下，抛出错误的问题
                const ruleLen = this.itemRules.rules && this.itemRules.rules.length
                if (!this.validator || !ruleLen || ruleLen === 0) return;

                // 检验时机
                // let trigger = this.isTrigger(this.itemRules.validateTrigger, this.validateTrigger, validateTrigger);
                const isRequiredField = _isRequiredField(this.itemRules.rules || []);
                let result = null;
                // 只有等于 bind 时 ，才能开启时实校验
                if (validateTrigger === 'bind' || formtrigger) {
                    // 校验当前表单项
                    result = await this.validator.validateUpdate({
                            [name]: value
                        },
                        formData
                    );

                    // 判断是否必填,非必填，不填不校验，填写才校验 ,暂时只处理 undefined  和空的情况
                    if (!isRequiredField && (value === undefined || value === '')) {
                        result = null;
                    }

                    // 判断错误信息显示类型
                    if (result && result.errorMessage) {
                        if (errShowType === 'undertext') {
                            // 获取错误信息
                            this.errMsg = !result ? '' : result.errorMessage;
                        }
                        if (errShowType === 'toast') {
                            uni.showToast({
                                title: result.errorMessage || '校验错误',
                                icon: 'none'
                            });
                        }
                        if (errShowType === 'modal') {
                            uni.showModal({
                                title: '提示',
                                content: result.errorMessage || '校验错误'
                            });
                        }
                    } else {
                        this.errMsg = ''
                    }
                    // 通知 form 组件更新事件
                    validateCheck(result ? result : null)
                } else {
                    this.errMsg = ''
                }
                return result ? result : null;
            },
            /**
             * 初始组件数据
             */
            init(type = false) {
                const {
                    validator,
                    formRules,
                    childrens,
                    formData,
                    localData,
                    _realName,
                    labelWidth,
                    _getDataValue,
                    _setDataValue
                } = this.form || {}
                // 对齐方式
                this.localLabelAlign = this._justifyContent()
                // 宽度
                this.localLabelWidth = this._labelWidthUnit(labelWidth)
                // 标签位置
                this.localLabelPos = this._labelPosition()
                // 将需要校验的子组件加入form 队列
                this.form && type && childrens.push(this)

                if (!validator || !formRules) return
                // 判断第一个 item
                if (!this.form.isFirstBorder) {
                    this.form.isFirstBorder = true;
                    this.isFirstBorder = true;
                }

                // 判断 group 里的第一个 item
                if (this.group) {
                    if (!this.group.isFirstBorder) {
                        this.group.isFirstBorder = true;
                        this.isFirstBorder = true;
                    }
                }
                this.border = this.form.border;
                // 获取子域的真实名称
                const name = _realName(this.name)
                const itemRule = this.userRules || this.rules
                if (typeof formRules === 'object' && itemRule) {
                    // 子规则替换父规则
                    formRules[name] = {
                        rules: itemRule
                    }
                    validator.updateSchema(formRules);
                }
                // 注册校验规则
                const itemRules = formRules[name] || {}
                this.itemRules = itemRules
                // 注册校验函数
                this.validator = validator
                // 默认值赋予
                this.itemSetValue(_getDataValue(this.name, localData))
            },
            unInit() {
                if (this.form) {
                    const {
                        childrens,
                        formData,
                        _realName
                    } = this.form
                    childrens.forEach((item, index) => {
                        if (item === this) {
                            this.form.childrens.splice(index, 1)
                            delete formData[_realName(item.name)]
                        }
                    })
                }
            },
            // 设置item 的值
            itemSetValue(value) {
                const name = this.form._realName(this.name)
                const rules = this.itemRules.rules || []
                const val = this.form._getValue(name, value, rules)
                this.form._setDataValue(name, this.form.formData, val)
                return val
            },

            /**
             * 移除该表单项的校验结果
             */
            clearValidate() {
                this.errMsg = '';
            },

            // 是否显示星号
            _isRequired() {
                // TODO 不根据规则显示 星号，考虑后续兼容
                // if (this.form) {
                // 	if (this.form._isRequiredField(this.itemRules.rules || []) && this.required) {
                // 		return true
                // 	}
                // 	return false
                // }
                return this.required
            },

            // 处理对齐方式
            _justifyContent() {
                if (this.form) {
                    const {
                        labelAlign
                    } = this.form
                    let labelAli = this.labelAlign ? this.labelAlign : labelAlign;
                    if (labelAli === 'left') return 'flex-start';
                    if (labelAli === 'center') return 'center';
                    if (labelAli === 'right') return 'flex-end';
                }
                return 'flex-start';
            },
            // 处理 label宽度单位 ,继承父元素的值
            _labelWidthUnit(labelWidth) {

                // if (this.form) {
                // 	const {
                // 		labelWidth
                // 	} = this.form
                return this.num2px(this.labelWidth ? this.labelWidth : (labelWidth || (this.label ? 70 : 'auto')))
                // }
                // return '70px'
            },
            // 处理 label 位置
            _labelPosition() {
                if (this.form) return this.form.labelPosition || 'left'
                return 'left'

            },

            /**
             * 触发时机
             * @param {Object} rule 当前规则内时机
             * @param {Object} itemRlue 当前组件时机
             * @param {Object} parentRule 父组件时机
             */
            isTrigger(rule, itemRlue, parentRule) {
                //  bind  submit
                if (rule === 'submit' || !rule) {
                    if (rule === undefined) {
                        if (itemRlue !== 'bind') {
                            if (!itemRlue) {
                                return parentRule === '' ? 'bind' : 'submit';
                            }
                            return 'submit';
                        }
                        return 'bind';
                    }
                    return 'submit';
                }
                return 'bind';
            },
            num2px(num) {
                if (typeof num === 'number') {
                    return `${num}px`
                }
                return num
            }
        }
    };
</script>

<style lang="scss">
    .uni-forms-item {
        position: relative;
        display: flex;
        /* #ifdef APP-NVUE */
        // 在 nvue 中，使用 margin-bottom error 信息会被隐藏
        padding-bottom: 22px;
        /* #endif */
        /* #ifndef APP-NVUE */
        margin-bottom: 22px;
        /* #endif */
        flex-direction: row;

        &__label {
            display: flex;
            flex-direction: row;
            align-items: center;
            text-align: left;
            font-size: 14px;
            color: #606266;
            height: 36px;
            padding: 0 12px 0 0;
            /* #ifndef APP-NVUE */
            vertical-align: middle;
            flex-shrink: 0;
            /* #endif */

            /* #ifndef APP-NVUE */
            box-sizing: border-box;

            /* #endif */
            &.no-label {
                padding: 0;
            }
        }

        &__content {
            /* #ifndef MP-TOUTIAO */
            // display: flex;
            // align-items: center;
            /* #endif */
            position: relative;
            font-size: 14px;
            flex: 1;
            /* #ifndef APP-NVUE */
            box-sizing: border-box;
            /* #endif */
            flex-direction: row;

            /* #ifndef APP || H5 || MP-WEIXIN || APP-NVUE */
            // TODO 因为小程序平台会多一层标签节点 ，所以需要在多余节点继承当前样式
            &>uni-easyinput,
            &>uni-data-picker {
                width: 100%;
            }

            /* #endif */

        }

        & .uni-forms-item__nuve-content {
            display: flex;
            flex-direction: column;
            flex: 1;
        }

        &__error {
            color: #f56c6c;
            font-size: 12px;
            line-height: 1;
            padding-top: 4px;
            position: absolute;
            /* #ifndef APP-NVUE */
            top: 100%;
            left: 0;
            transition: transform 0.3s;
            transform: translateY(-100%);
            /* #endif */
            /* #ifdef APP-NVUE */
            bottom: 5px;
            /* #endif */

            opacity: 0;

            .error-text {
                // 只有 nvue 下这个样式才生效
                color: #f56c6c;
                font-size: 12px;
            }

            &.msg--active {
                opacity: 1;
                transform: translateY(0%);
            }
        }

        // 位置修饰样式
        &.is-direction-left {
            flex-direction: row;
        }

        &.is-direction-top {
            flex-direction: column;

            .uni-forms-item__label {
                padding: 0 0 8px;
                line-height: 1.5715;
                text-align: left;
                /* #ifndef APP-NVUE */
                white-space: initial;
                /* #endif */
            }
        }

        .is-required {
            // color: $uni-color-error;
            color: #dd524d;
            font-weight: bold;
        }
    }


    .uni-forms-item--border {
        margin-bottom: 0;
        padding: 10px 0;
        // padding-bottom: 0;
        border-top: 1px #eee solid;

        /* #ifndef APP-NVUE */
        .uni-forms-item__content {
            flex-direction: column;
            justify-content: flex-start;
            align-items: flex-start;

            .uni-forms-item__error {
                position: relative;
                top: 5px;
                left: 0;
                padding-top: 0;
            }
        }

        /* #endif */

        /* #ifdef APP-NVUE */
        display: flex;
        flex-direction: column;

        .uni-forms-item__error {
            position: relative;
            top: 0px;
            left: 0;
            padding-top: 0;
            margin-top: 5px;
        }

        /* #endif */

    }

    .is-first-border {
        /* #ifndef APP-NVUE */
        border: none;
        /* #endif */
        /* #ifdef APP-NVUE */
        border-width: 0;
        /* #endif */
    }
</style>